#ifndef EXPRESSION_H
#define EXPRESSION_H
#include "AutoPointer.h"
#include "Pattern.h"
#include "Context.h"
#include <typeinfo>
#include <string>
#include <list>

namespace TreeExpression {

class Expression {
	public:
		Expression() { }
		Expression(const Expression& copy) : leaf(copy.leaf), son(copy.son) { }
		virtual ~Expression() { }

		virtual AutoPointer<Expression> eval(CopyPointer<Context>& context) = 0;
		virtual Expression* clone() const = 0;
		Expression* cloneTree() const {
			Expression* ris = this->clone();
			if (!leaf.isNull()) ris->leaf.set(leaf->cloneTree());
			if (!son.isNull()) ris->son.set(son->cloneTree());
			return ris;
		}

		virtual std::string getHash() const { return typeid(*this).name(); }

		bool hasSon() const {
			return !son.isNull();
		}

		bool hasLeaf() const {
			return !leaf.isNull();
		}

		void setSon(const AutoPointer<Expression>& toAdd) {
			son = toAdd;
		}

		AutoPointer<Expression> getSon() const {
			return son;
		}

		AutoPointer<Expression> getLeaf() const {
			return leaf;
		}

		void addLeaf(const AutoPointer<Expression>& toAdd) {
			if (leaf.isNull()) leaf = toAdd;
			else leaf->addLeaf(toAdd);
		}

		virtual std::string toString() const = 0;

		void evalTree(CopyPointer<Context>& context) {
			if (!leaf.isNull()) {
				AutoPointer<Expression> subSon = leaf->getSon();
				AutoPointer<Expression> subLeaf = leaf->getLeaf();
				leaf = leaf->eval(context);
				leaf->son = subSon;
				leaf->leaf = subLeaf;
				leaf->evalTree(context);
			}
			if (!son.isNull()) {
				AutoPointer<Expression> subSon = son->getSon();
				AutoPointer<Expression> subLeaf = son->getLeaf();
				son = son->eval(context);
				son->leaf = subLeaf;
				son->son = subSon;
				son->evalTree(context);
			}
		}

	private:
		AutoPointer<Expression> leaf; //this pointer points to the leaf of the same level
		AutoPointer<Expression> son; //this pointer points to the son
};

template<typename  Derived> class Expression_CRTP: public virtual Expression {
	public:
		Expression_CRTP() : Expression() { }
		Expression_CRTP(const Expression_CRTP& copy) : Expression(copy) { }

    	Expression* clone() const {
        	return new Derived(dynamic_cast<Derived const&>(*this));
    	}
};

}


#endif
